macro 'Make Movie to Disk';
{
Captures images at a specified rate and saves them to disk.
Select an area of interest within the Camera window before
starting. Abort at any time by pressing the mouse button.
}
var
  nFrames,n,Left,Top,Width,Height:integer;
  interval,EndTicks,secs:integer;
  FirstTime:boolean;
begin
  GetRoi(Left,Top,Width,Height);
  if width=0 then begin
     PutMessage('First select the area of interest in the Camera window.');
     exit;
  end;
  nFrames:=GetNumber('Number of Frames?',10);
  secs:=GetNumber ('Interval Between Frames (seconds)?',60.0);
  interval:=round(secs*60);
  FirstTime:=true;
  for n:=1 to nFrames do begin
      StopCapturing;
       beep;
       MakeRoi(Left,Top,Width,Height);
       SaveAs('Frame ',n);
      if FirstTime then begin
          EndTicks:=TickCount+interval;
          FirstTime:=false;
      end;
      if button then begin
         StopCapturing;
         exit;
      end;
      StartCapturing;
      while TickCount < EndTicks do begin
          secs:=(EndTicks - TickCount) div 60;
          ShowMessage(n:1, '/', nFrames,' ', secs:4)
      end;
      EndTicks:=EndTicks+interval;
  end;
  StopCapturing;
end;


macro 'Capture Averaged to Stack';
var
  x,y,i,s,f,t,width,height:integer;
  camera, stack, avg, nFrames:integer;
begin
  nFrames:=GetNumber('Number of frames to capture:', 10);
  SelectWindow('Camera');
  camera:=PidNumber;
  GetRoi(x,y,width,height);
   if width=0 then begin
    PutMessage('Rectangular selection required.');
    exit;
  end;
  SetNewSize(width,height);
  MakeNewStack('Stack');
  stack:=PidNumber;
  s:=TickCount;
  for i:= 1 to nFrames do begin
     ChoosePic(Camera);
     MakeRoi(x,y,width,height);
     AverageFrames;
     RestoreRoi;
     Copy;
     ChoosePic(stack);
     if i>1 then AddSlice;
     Paste;
  end;
  f:=TickCount;
  KillRoi;
  SelectSlice(1);
  t:=(f-s)/60;
  ShowMessage('Time = ', t:1:2, ' seconds');
  exit;
  AverageSlices;
  avg:=PidNumber;
  SelectPic(stack);
  Dispose;
  SelectPic(avg);
end;

procedure PlotFrameIntervals(nFrames: integer);
var
   xmin,xmax,ymin,ymax,i,xscale,yscale,yscale2:real;
   width,height,margin,pwidth,pheight:integer;
   x,y,pbottom,yinc:integer;
   minInterval,maxInterval,offset:real;
   interval,reqInterval,avgInterval,ElapsedTime:real;
   TotalTime:real;
begin
  RequiresVersion(1.58);
  SaveState;
  margin:=40;
  width:=500;
  height:=300;
  ymin:=999999;
  ymax:=-999999;
  ElapsedTime := PlotData[nFrames];
  reqInterval := PlotData[nFrames+1];
  avgInterval := ElapsedTime / (nFrames -1);
  TotalTime := avgInterval * nFrames;
  for i:=1 to nFrames -1 do begin
      interval := PlotData[i+1]-PlotData[i];
      if interval<ymin then ymin:=interval;
      if interval>ymax then ymax:=interval;
  end;
  minInterval:=ymin;
  maxInterval:=ymax;
  ymin:=0;
  xmin:=1;
  xmax:=nFrames-1;
  SetNewSize(width,height);
  SetForeground(255);
  SetBackground(0);
  MakeNewWindow('Frame Intervals (seconds)');
  pwidth:=width-margin-130;
  pheight:=height-2*margin;
  pbottom:=margin+pheight;
  xscale:=pwidth/xmax;
  yscale:=pheight/ymax;
  yscale2:=pheight/(PlotData[nFrames]);
  SetForeground(255);
  SetBackground(0); 
  SetLineWidth(1);
  for i:=2 to nFrames do begin
     interval := PlotData[i]-PlotData[i-1];
     x:=margin+(i-1)*xscale;
     y:=pbottom-interval*yscale;
     MoveTo(x, pBottom);
     LineTo(x,y);
     MoveTo(margin+(i-2)*xscale, pBottom-(PlotData[i-1])*yscale2);
     LineTo(margin+(i-1)*xscale, pBottom-(PlotData[i])*yscale2);
  end;
  KillRoi;
  MoveTo(margin, margin);
  LineTo(margin, margin+pheight);
  LineTo(margin+pwidth, margin+pheight);
  SetFont('Geneva');
  SetFontSize(9);
  SetText('Right Justified');
  MoveTo(margin-2, margin+pheight-5);
  writeln(ymin:1:3);
  MoveTo(margin-2, margin);
  writeln(ymax:1:3);
  SetText('Left Justified');
  x := margin+pwidth+10;
  y := margin;
  yinc := 12;
  MoveTo(x, y);
  writeln('frames=', nFrames:1);
  y := y+yinc;
  MoveTo(x, y);
  writeln('expected time=', nFrames*reqInterval:1:4);
  y := y+yinc;
  MoveTo(x, y);
  writeln('actual time=', TotalTime:1:4);
  y := y+yinc;
  MoveTo(x, y);
  writeln('req. interval=', reqInterval:1:4);
  y := y+yinc;
  MoveTo(x, y);
  writeln('avg interval=', avgInterval:1:4);
  y := y+yinc;
  MoveTo(x, y);
  writeln('min interval=', minInterval:1:4);
  y := y+yinc;
  MoveTo(x, y);
  writeln('max interval=', maxInterval:1:4);
  y := y+yinc;
  MoveTo(x, y);
  if reqInterval <> 0.0 then
     writeln('expected rate=', 1/reqInterval:1:3,' fps');
  y := y+yinc;
  MoveTo(x, y);
  writeln('actual rate=', (nFrames)/TotalTime:1:3,' fps');
 RestoreState;
end;

macro 'Make Movie and Plot Intervals [M]';
var
  i, nFrames, x, y, w, h: integer;
  avgInterval: real;
begin
  GetRoi(x, y, w, h);
  if w = 0 then begin
     PutMessage('Selection Required.');
     exit;
  end;
  MakeMovie('dialog, time-stamp', -1, -1);
  nFrames := PlotData[0];
  PlotFrameIntervals(nFrames); 
end;


Macro 'Frame Rate vs. Frame Size';
var
  n, i, width, height,w,h:integer;
  TextWidth, TextHeight: integer;
  avgFrameInterval: real;
begin
   RequiresVersion(1.61);
   n := 50;
   TextWidth := 150;
   TextHeight := 400;
   StartCapturing;
   GetPicSize(width, height);
   SaveState;
   SetFont('Monaco');
   SetFontSize(9);
   NewTextWindow('Rate vs. Size', TextWidth, TextHeight);
   MoveWindow(get('ScreenWidth') - TextWidth - 10, 50);
   for i := 1 to n do begin
     SelectWindow('Camera');
     w := round(width*(i/n));
      h := round(height*(i/n));
      w := w - (w mod 4);
      h := h - (h mod 4);
     MakeRoi(0, 0, w, h);
     MakeMovie('blind', 10, 0);
     avgFrameInterval := GetSliceSpacing;
     Dispose;
     SelectWindow('Rate vs. Size');
     writeln(i:3, avgFrameInterval:6:3, '  ', w:1:0, 'x', h:1:0);
  end;
  RestoreState;
end;


macro 'Projection Example';
begin
   SetProjection('Initial Angle', 0);
   SetProjection('Total Rotation', 360);
   SetProjection('Rotation Increment', 30);
   SetDensitySlice(0, 254);   {sets transparency bounds}
   SetProjection('Surface Opacity', 50);
   SetProjection('Surface Depth-Cueing', 50);
   SetProjection('Interior Depth-Cueing', 50);
   SetProjection('Save Projections', false);
   SetProjection('Minimize Size', true);
   SetProjection('Y-Axis');
   SetProjection('Brightest Point');
   Project; {Dialog is not displayed}
end.


macro '(-' begin end;


macro 'Average of Stack...';
var
  i, j, nFrames, srcStack, dstStack: integer;
  n, stack, width, height: integer;
begin
  RequiresVersion(1.62);
  if nSlices=0 then
    exit('Stack required.');
  SaveState;
  GetPicSize(width, height);
  nFrames := getNumber('Number of Frames to Average:', 10, 0);
  n := nSlices div nFrames;
  if n = 0 then
    exit;
  srcStack := PidNumber;
  SetNewSize(width,height);
  MakeNewStack(WindowTitle, ' (Averaged)');
  dstStack := PidNumber;
  ChoosePic(srcStack);
  for i := 1 to n do begin
    SelectSlice(1);
    AverageSlices(1, nFrames);
    SelectAll;
    Copy;
    Dispose;
    ChoosePic(dstStack);
    if i>1 then AddSlice;
    Paste;
    ChoosePic(srcStack);
    for j := 1 to nFrames do begin
      SelectSlice(1);
      DeleteSlice;
    end;
  end;
  SelectPic(srcStack);
  dispose;
  RestoreState;
end;


macro 'Running Average of Stack...';
var
  i, nFrames: integer;
begin
  RequiresVersion(1.62);
  if nSlices=0 then
    exit('Stack required.');
  nFrames := getNumber('Number of Frames to Average:', 10, 0);
  for i := 1 to nSlices - nFrames + 1 do begin
    SelectSlice(i);
    AverageSlices(i, nFrames);
    SelectAll;
    Copy;
    Dispose;
    Paste;
  end;
end;


procedure DeInterlace(OddSlice: boolean);
{
If OddSlice is true, each even line is replaced by the adjacent odd line, otherwise each odd line is replaced by the adjacent even line.
}
var
  i,width,height,row1,row2:integer;
begin
  GetPicSize(width,height);
  if OddSlice then begin
     i := 1;
     while i < (height - 2) do begin
        GetRow(0, i, width);
        PutRow(0, i - 1, width);
        i := i + 2;
     end;
  end else begin
     i := 0;
     while i < (height - 1) do begin
        GetRow(0, i, width);
        PutRow(0, i + 1, width);
        i := i + 2;
     end;
  end;
end;

macro 'De-interlace Stack';
{Converts 30fps movies to 60 fields per second.}
var
  i, width, height:integer;
begin
  RequiresVersion(1.61);
  if nSlices=0 then
    exit('Stack required.');
  i:=nSlices;
  GetPicSize(width,height);
  if (i * width * height) > get('freemem') then
    exit('There is not enough free RAM to de-interlace this stack.');
  repeat
     SelectSlice(i);
     SelectAll;
     Copy;
     AddSlice;
     Paste;
     i:=i - 1;
  until i = 0;
  KillRoi;
  for i := 1 to nSlices do begin
     SelectSlice(i);
     DeInterlace(odd(i));
  end;
end;


macro 'Add Timestamps to Stack...';
{By Steve Potter (spotter@gg.caltech.edu).  Adds a timestamp to each frame of a
stack of time-lapse images.}

var
  i,j,k,interval, frameNumber, hours, minutes, seconds, xpos, ypos, colon1pos,
colon2pos, ones, tens: integer;
  militaryTime, printSeconds: boolean;
  StartTime, name, textStamp,tempStr, hourStr, minStr, secStr, AMPMstr, timeStr,
oneStr, tenStr: string;

begin
  if nSlices=0 then begin
    PutMessage('Stack required.');
    exit
  end;
  ShowMessage('Time Stamps:\\Make sure you have already selected the desired font, fontsize, and drawcolor.\Use AM or PM if 12-h clock is desired, e.g. "3:07 PM".  Use two colons if you wish to display seconds, e.g. "3:07:22 PM".\The point you click will be the upper-left corner of the time-stamp.');
  interval:=GetNumber('Time interval (seconds): ',300);
  StartTime:=GetString('Enter the start time.  ','0:00:00');
  MilitaryTime:=true;
  AMPMstr:='';
  if (pos('AM',StartTime)<>0) then MilitaryTime:=false;
  if (pos('AM',StartTime)<>0) then AMPMstr:=' AM';
  if (pos('PM',StartTime)<>0) then MilitaryTime:=false;
  if (pos('PM',StartTime)<>0) then AMPMstr:=' PM';

  hourStr:=StartTime;
  colon1pos:=pos(':', StartTime);
  Delete(hourStr,colon1pos, 10); {get rid of everything but the hours}
  hours:=StringToNum(hourStr);
  minStr:=StartTime;
  Delete(minStr, 1, colon1pos);  {remove the hours}
  colon2pos:=pos(':',minStr);
  if (colon2pos<>0) then printSeconds:=true else printSeconds:=false;
  Delete(minStr,3, 7);  {get rid of everything but the minutes}
  minutes:=StringToNum(minStr);
  seconds:=0;
 If printSeconds then
 begin
  secStr:=StartTime;
  Delete(secStr,1, colon2pos);
  Delete(secStr,3, 5);   {get rid of all but the seconds}
  seconds:=StringToNum(secStr);
 end;

  frameNumber:=GetNumber('Enter the number of the first frame:',1);
  name:=GetString('Enter your name or initials: ');
  textStamp:=GetString('Date, experimental conditions, etc.: ','');
  PutMessage('Click at desired text location...');
  SelectSlice(1);
  repeat
    GetMouse(xpos,ypos);
  until Button;
  For i:=1 to nSlices do
  begin
     SelectSlice(i);
     MoveTo(xpos,ypos);
     ones:=minutes mod 10;
     oneStr:=chr(ones+48); {48 is the ASCII value of the character 0}
     tens:=minutes div 10;
     tenStr:=chr(tens+48);
     minStr:=concat(tenStr, oneStr); {it is a shame I cant find a num-2-string function!}
     if printSeconds then
      begin
       ones:=seconds mod 10;
       oneStr:=chr(ones+48); {48 is the ASCII value of the character 0}
       tens:=seconds div 10;
       tenStr:=chr(tens+48);
       secStr:=concat(tenStr, oneStr);
      end;
     timeStr:=concat(hours,':',minStr);
     if printSeconds then timeStr:=concat(timeStr, ':',secStr);
     Writeln(textStamp,'    ',name);
     Write(frameNumber, '    ',timeStr, AMPMstr);
     frameNumber:=frameNumber+1;
     seconds:=seconds+interval;
     minutes:=minutes+(seconds div 60);
     seconds:=seconds mod 60;
     hours:=hours +(minutes div 60);
     minutes:=minutes mod 60;
     if militaryTime then if (hours>24) then hours:=hours-24;
     if ((not(militaryTime)) AND (hours>11)) then
       begin
         if (AMPMstr=' AM') then AMPMstr:= ' PM';
         if (AMPMstr=' PM') then AMPMstr:= ' AM';
       end;
      if ((not(militaryTime)) AND (hours>12)) then hours:=hours-12;
  end;
end; {Time Stamps...}



